package com.jokerpan.project.filter;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jokerpan.project.Message.Msg;
import com.jokerpan.project.util.JsonToMap;
import com.jokerpan.project.util.JwtUtils;
import lombok.SneakyThrows;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * 验证用户名密码正确后，生成一个token，并将token返回给客户端
 * 该类继承自UsernamePasswordAuthenticationFilter，重写了其中的2个方法 ,
 * attemptAuthentication：接收并解析用户凭证。
 * successfulAuthentication：用户成功登录后，这个方法会被调用，我们在这个方法里生成token并返回。
 */
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter{

    private AuthenticationManager authenticationManager;

    /**
     * security拦截默认是以POST形式走/login请求，我们这边设置为走/token请求
     * get /token
     * @param authenticationManager
     */
    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        super.setFilterProcessesUrl("/oauth/login");
    }

    /**
     * 接收并解析用户凭证
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @SneakyThrows
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        // 从输入流中获取到登录的信息
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        return authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(username, password)
        );

    }

    // 成功验证后调用的方法
    // 如果验证成功，就生成token并返回
    @SneakyThrows
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        Object jwtUser = authResult.getPrincipal();
        ObjectMapper objectMapper = new ObjectMapper();
        String value = objectMapper.writeValueAsString(jwtUser);
        value = value.replace("null", "\"\"");
        Map map = JsonToMap.jsonStr2Map(value);

        String role = "";
        List<HashMap<String, Object>> authorities = (List<HashMap<String, Object>>) map.get("authorities");
        for (HashMap<String, Object> obj: authorities) {
            role += obj.get("authority")+ ",";
        }
        String username = map.get("username").toString();
        String token = JwtUtils.createToken(username, role);

        // 返回创建成功的token  但是这里创建的token只是单纯的token
        // 按照jwt的规定，最后请求的时候应该是 `Bearer token`
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        String tokenStr = JwtUtils.TOKEN_PREFIX + token;
        response.setHeader("token", tokenStr);
        //返回用户信息与token
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("token", token);
        Msg result = Msg.success("登录成功!", resultMap);
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(JSON.toJSONString(result));
    }

    // 失败 返回错误就行
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
        String msg = "";
        if (e instanceof AccountExpiredException) {
            msg = "账号过期";
        } else if (e instanceof BadCredentialsException) {
            msg = "用户名或密码错误";
        } else if (e instanceof CredentialsExpiredException) {
            msg = "密码过期";
        } else if (e instanceof DisabledException) {
            msg = "账号不可用";
        } else if (e instanceof LockedException) {
            msg = "账号已被锁定";
        } else if (e instanceof InternalAuthenticationServiceException) {
            msg = "用户不存在";
        }else{
            //其他错误
            msg = "未知错误";
        }
        Msg result = Msg.fail(msg);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write(JSON.toJSONString(result));
    }

}
